Lancet
Lancet 引入
什么是 Lancet?
Lancet 是一个轻量级 Android AOP 框架,基于 ASM
- 编译速度快, 并且支持增量编译.
- 简洁的 API, 几行 Java 代码完成注入需求.
- 没有任何多余代码插入 apk.
- 支持用于 SDK, 可以在 SDK 编写注入代码来修改依赖 SDK 的 App.
lancet 引入
- 目前最新官方版本 1.0.6 的很久不维护了,不支持 AMS6,在高版本的 AGP 上会报错,一般自己要用得自己修改支持
- 支持 AGP7.x,Transform 废弃
lancet 升级ASM9.1 , 适配Gradle7.5.1 AGP7.3.0 ,调整支持Java11
- AGP8.x AGP 的 Transform API 移除,用不了了
Gradle
// top gradle.build.kts
buildscript {
dependencies {
//noinspection UseTomlInstead
classpath("me.ele:lancet-plugin:2.0.0")
}
}
// app gradle.build.kts
plugins {
id("me.ele.lancet")
}
和 AspectJ 对比
Lancet 本质上是 Gradle Plugin,通过依赖 Android 的打包插件提供的 Transform API,在打包过程中获取到所有的代码。
依赖 ASM 提供的字节码注入能力,通过我们解析自定义的注解,在目标点注入相应的代码。
通过注解进行 AOP 这点和 AspectJ 很相似,但是更加轻量和简洁,使用方式也有所不同。
这里要区分一个概念,编译期注入和运行期注入。
编译期:即在编译时对字节码做插桩修改,达到 AOP 的目的。优点是运行时无额外的性能损耗,但因为编译时的限制,只能修改最终打包到 APK 中的代码,即 Android Framework 的代码是固化在 ROM 中的,无法修改。
运行期:是只在运行时动态的修改代码的执行,因而可以修改 Framework 中代码的执行流程,在 hook 点上执行性能上有所损耗。
- Lancet,编译期注入
- AspectJ,既支持编译期也支持运行期的注入,运行期的注入一般是依赖 JVM 提供的 AttachAPI,因为
Android 没有 JVM 的环境,实际上 class 还会继续转换成 dex,因此 AspectJ 在 Android 平台是只能做到编译期注入。
- Dexposed,运行期注入
使用
- Transform 生成的 class 文件放在
build/intermediates/transforms/lancet/debug - 可以在 module 或者 library 中使用,在 module 中定义的注解,在 APK 打包时才会被解析,因此在写
module 时不要 apply plugin: me.ele.lancet
代码织入方式
@Proxy
@Proxy 介绍
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Proxy {
String value();
}
将使用新的方法替换代码里存在的原有的目标方法
应用场景:通常用于对系统 API 的劫持。因为虽然我们不能注入代码到系统提供的库之中,但我们可以劫持掉所有调用系统 API 的地方。
@Proxy 示例
示例:在 Log.d 输出,增加当前调用者的线程信息
@Proxy("d")
@TargetClass("android.util.Log")
public static int anyName(String tag, String msg) {
tag = "hacket123." + tag;
msg = msg + "-->>" + Thread.currentThread().getName();
return (int) Origin.call();
}
在 Activity 的 onCreate 调用 Log.d,
Log.d("hacket","onCreate。。。。。。")
输出:
onCreate。。。。。。-->>main
反编译后:在原有的 msg 后增加了线程信息,原有方法调用替换成 包.aop注解的方法
public final class MainActivity extends ComponentActivity {
public static final int $stable = LiveLiterals$MainActivityKt.INSTANCE.m5476Int$classMainActivity();
/* loaded from: classes4.dex */
class _lancet {
private _lancet() {
}
@Proxy("d")
@TargetClass("android.util.Log")
static int com_example_lancetdemos_lancet_LancetAop_anyName(String str, String str2) {
SystemClock.elapsedRealtime();
return Log.d("hacket123." + str, str2 + "-->>" + Thread.currentThread().getName());
}
}
/* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_lancet.com_example_lancetdemos_lancet_LancetAop_anyName(LiveLiterals$MainActivityKt.INSTANCE.m5477String$arg0$calld$funonCreate$classMainActivity(), LiveLiterals$MainActivityKt.INSTANCE.m5478String$arg1$calld$funonCreate$classMainActivity());
androidx.activity.compose.ComponentActivity.setContent$default(this, null, ComposableSingletons$MainActivityKt.INSTANCE.m5473getLambda3$app_debug(), 1, null);
}
}
@NameRegex
@NameRegex 用来限制范围操作的作用域. 仅用于 Proxy 模式中, 比如你只想代理掉某一个包名下所有的目标操作. 或者你在代理所有的网络请求时,不想代理掉自己发起的请求. 使用 NameRegex 对 @TargetClass , @ImplementedInterface 筛选出的 class 再进行一次匹配。
@Insert
@Insert 介绍
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Insert {
String value();
boolean mayCreateSuper() default false;
}
- 将新代码插入到目标方法原有代码前后。
- 常用于操作 App 与 library 的类,并且可以通过
This操作目标类的私有属性与方法 - @Insert 当目标方法不存在时,还可以使用
mayCreateSuper参数来创建目标方法。
@Insert 介绍
示例:注入每一个 Activity 的 onStop 生命周期
@TargetClass(value = "android.support.v7.app.AppCompatActivity", scope = Scope.LEAF)
@Insert(value = "onStop", mayCreateSuper = true)
protected void onStop(){
System.out.println("hello world");
Origin.callVoid();
}
- scope 目标是 AppCompatActivity 的所有最终子类
- 如果一个类 MyActivity extends AppcompatActivity 没有重写 onStop 会自动创建 onStop 方法,而 Origin 在这里就代表了 super.onStop(),最终注入效果:
protected void onStop() {
System.out.println("hello world");
super.onStop();
}
- 注入后的方法的修饰符(public/protected/private)会完全照搬注解修饰的 Hook 方法的修饰符
匹配目标类
public @interface TargetClass {
String value();
Scope scope() default Scope.SELF;
}
public @interface ImplementedInterface {
String[] value();
Scope scope() default Scope.SELF;
}
public enum Scope {
SELF,
DIRECT,
ALL,
LEAF
}
- TargetClass 类
- ImplementedInterface 接口
@TargetClass 通过类
- value 是一个类的全名
- scope
- Scope.SELF 代表仅匹配 value 指定的目标类本身
- Scope.DIRECT 代表匹配 value 指定类的直接子类
- Scope.All 代表匹配 value 指定类的所有子类
- Scope.LEAF 代表匹配 value 指定类的最终子类。Java 是单继承,所以继承关系是树形结构,所以这里代表了指定类为顶点的继承树的所有叶子节点.
@ImplementedInterface
- value 可以填写多个接口的全名
- scope
- Scope.SELF : 代表直接实现所有指定接口的类
- Scope.DIRECT : 代表直接实现所有指定接口,以及指定接口的子接口的类
- Scope.ALL: 代表 Scope.DIRECT 指定的所有类及他们的所有子类
- Scope.LEAF: 代表 Scope.ALL 指定的森林结构中的所有叶节点

示例:当我们使用@ImplementedInterface(value = "I", scope = …) 时, 目标类如下:
- Scope.SELF -> A
- Scope.DIRECT -> A C
- Scope.ALL -> A B C D
- Scope.LEAF -> B D
如何获取类的全名
- 右键类,Copy→Copy Reference
- this.getClass() 看日志输出
- javap 命令
- 进入 app/build/intermediates/javac/debug/classes/com/example/lancetdemos
- javap -c MyRunnable
- 三方库,用 jadx-gui,上面会注释内部类的全路径名


匿名内部类 com.appsflyer.internal.AFd1pSDK.4�是不对的,正确的应该是 com.appsflyer.internal.AFd1pSDK$4
- ClassyShark
java -jar $HACK_HOME/ClassyShark.jar -open
匹配目标方法
虽然在 Proxy , Insert 中我们指定了方法名, 但识别方法必须要更细致的信息. 我们会直接使用 Hook 方法的修饰符,参数类型来匹配方法.
所以一定要保持 Hook 方法的 public/protected/private static 信息与目标方法一致,参数类型,返回类型与目标方法一致.
返回类型可以用 Object 代替.
方法名不限. 异常声明也不限.
@ClassOf
没有权限声明目标类,用@ClassOf 注解来替代对类的直接 import。
ClassOf 的 value 一定要按照 (package_name.)(outer_class_name$)inner_class_name([]…) 的模板。
比如:
- java.lang.Object
- java.lang.Integer[][]
- A[]
- A$B
示例:
public class A {
protected int execute(B b){
return b.call();
}
private class B {
int call() {
return 0;
}
}
}
@TargetClass("com.dieyidezui.demo.A")
@Insert("execute")
public int hookExecute(@ClassOf("com.dieyidezui.demo.A$B") Object o) {
System.out.println(o);
return (int) Origin.call();
}
API
Origin
Origin 用来调用原目标方法,可以被多次调用。
- Origin.call() 用来调用有返回值的方法
- Origin.callVoid() 用来调用没有返回值的方法
如果你有捕捉异常的需求,可以使用:
- Origin.call/callThrowOne/callThrowTwo/callThrowThree()
- Origin.callVoid/callVoidThrowOne/callVoidThrowTwo/callVoidThrowThree()
This
仅用于 @Insert 方式的非静态方法的 Hook 中,否则报错

- get() 返回目标方法被调用的实例化对象
- putField & getField
你可以直接存取目标类的所有属性,无论是 protected or private.
另外,如果这个属性不存在,我们还会自动创建这个属性. Exciting!
自动装箱拆箱肯定也支持了.
注意:
- Proxy 不能使用 This
- 你不能存取你父类的属性. 当你尝试存取父类属性时,我们还是会创建新的属性
package me.ele;
public class Main {
private int a = 1;
public void nothing(){
}
public int getA(){
return a;
}
}
@TargetClass("me.ele.Main")
@Insert("nothing")
public void testThis() {
Log.e("debug", This.get().getClass().getName());
This.putField(3, "a");
Origin.callVoid();
}
示例
匿名内部类
public class StaticClassTest {
public static class InnerClass {
public int i;
public void test() {
System.out.println("InnerClass test");
}
}
}
hook:
@Insert("test")
@TargetClass(value = "com.example.lancetdemos.StaticClassTest$InnerClass", scope = Scope.SELF)
public void test() {
StaticClassTest.InnerClass innerClass = (StaticClassTest.InnerClass) This.get();
innerClass.i = 100;
System.out.println("StaticClassTest.InnerClass test aop i=" + innerClass.i);
Origin.callVoid();
}
反编译:
捕获 MyRunnable 崩溃
原始代码:
public class MyRunnable implements Runnable {
@Override
public void run() {
Log.w("hacket", "MyRunnable run...");
try {
Thread.sleep(3000);
Log.d("hacket", "MyRunnable run sleep 3 end...");
int i = 1 / 0;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
hook:
@TargetClass(value = "com.example.lancetdemos.MyRunnable", scope = Scope.SELF)
@Insert(value = "run", mayCreateSuper = false)
protected void run() {
Log.d("hacket", "MyRunnable aop run.");
try {
Origin.callVoid();
} catch (Exception e) {
e.printStackTrace();
}
}
用 jadx-gui 反编译看看:
- 原有的 run 方法替换成了
_lancet.com_example_lancetdemos_lancet_LancetAop_run(this);,这里我们进行了 try catch
方法内的方法捕获
原代码:
public class MyRunnable implements Runnable {
@Override
public void run() {
Log.w("hacket", "MyRunnable run...");
try {
Thread.sleep(3000);
Log.d("hacket", "MyRunnable run sleep 3 end...");
// int i = 1 / 0;
} catch (InterruptedException e) {
e.printStackTrace();
}
class InnerRunnable implements Runnable {
@Override
public void run() {
Log.i("hacket", "InnerRunnable run start. class=" + this.getClass());
String s = null;
s.toLowerCase();
Log.d("hacket", "InnerRunnable run end...");
}
}
InnerRunnable innerRunnable = new InnerRunnable();
innerRunnable.run();
}
}
hook 代码:
@TargetClass(value = "com.example.lancetdemos.MyRunnable$1InnerRunnable", scope = Scope.SELF)
@Insert(value = "run", mayCreateSuper = false)
protected void run() {
Log.d("hacket", "MyRunnable$InnerRunnable aop run add try catch.");
try {
Origin.callVoid();
} catch (Exception e) {
e.printStackTrace();
}
}
callThrowOne 示例
// 原始
public abstract class InputStream implements Closeable {
public abstract int read() throws IOException;
}
@TargetClass("java.io.InputStream")
@Proxy("read")
public int read(byte[] bytes) throws IOException {
try {
return (int) Origin.<IOException>callThrowOne();
} catch (IOException e) {
e.printStackTrace();
throw e;
}
}
修复三方 sdk 的 crash
appsflyer 崩溃

hook 代码:
@TargetClass(value = "com.appsflyer.internal.AFd1pSDK$4", scope = Scope.SELF)
@Insert(value = "run", mayCreateSuper = false)
protected void run1() {
Log.d("hacket", "com.appsflyer.internal.AFd1pSDK.4 aop run add try catch.");
try {
Origin.callVoid();
} catch (Exception e) {
e.printStackTrace();
}
}
hook 前代码:
hook 后代码:
插桩 OkHttp 添加 Flipper 的 Interceptor
public class OkHttpHook {
// hook okhttp,添加一个拦截器
@Insert("build")
@TargetClass("okhttp3.OkHttpClient$Builder")
public OkHttpClient hookBuild() {
System.out.println("hook okhttp");
OkHttpClient.Builder builder = (OkHttpClient.Builder) This.get();
builder.addNetworkInterceptor(FlipperTool.getFlipperOkhttpInterceptor());
OkHttpClient client = (OkHttpClient) Origin.call();
return client;
}
}
修复案例
public class CrashHook {
//捕捉ConnectionTracker中unbindService出现异常的情况
@Insert("unbindService")
@TargetClass("com.google.android.gms.common.stats.ConnectionTracker")
public void changeUnbindService(Context var1, ServiceConnection var2) { //getGoogleAnalytics
try {
Origin.callVoid();
} catch (Throwable e) {
e.printStackTrace();
}
}
//捕捉RecyclerView动画崩溃问题,
@TargetClass("androidx.recyclerview.widget.SimpleItemAnimator")
@Insert("dispatchChangeFinished")
public void changeUnbindService(RecyclerView.ViewHolder item, boolean oldItem) { //getGoogleAnalytics
try {
Origin.callVoid();
} catch (Throwable e) {
e.printStackTrace();
}
}
// @TargetClass("com.alibaba.android.arouter.launcher._ARouter")
// @Insert("startActivity") //可能是options出错了,去掉options重试
// private void startRouterActivity(int requestCode,
// Context currentContext,
// Intent intent,
// Postcard postcard,
// NavigationCallback callback) {
// if (requestCode >= 0) { // Need start for result
// if (currentContext instanceof Activity) {
// try {
// ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
// } catch (Throwable e) {
// ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, null);
// FirebaseCrashlyticsProxy.INSTANCE.recordException(e);
// }
// } else {
// Log.w(Consts.TAG, "Must use [navigation(activity, ...)] to support [startActivityForResult]");
// }
// } else {
// try {
// ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());
// } catch (Throwable e) {
// ActivityCompat.startActivity(currentContext, intent, null);
// FirebaseCrashlyticsProxy.INSTANCE.recordException(e);
// }
// }
// if ((-1 != postcard.getEnterAnim() && -1 != postcard.getExitAnim()) && currentContext instanceof Activity) { // Old version.
// try {
// ((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());
// } catch (Throwable e) {
// e.printStackTrace();
// FirebaseCrashlyticsProxy.INSTANCE.recordException(e);
// }
// }
// if (null != callback) { // Navigation over.
// callback.onArrival(postcard);
// }
// }
@TargetClass("okio.RealBufferedSink")
@Insert("flush") //可能是options出错了,去掉options重试
public void okioFlush() throws IOException {
try {
Origin.callVoid();
} catch (Throwable e) {
if (e instanceof IOException) {
throw e;
} else {
throw new IOException(e);
}
}
}
//线上Google play日志,数据库打开有少量报错,firebase日志记录分析原因
// @Insert("getWritableDatabase")
// @TargetClass("androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper")
// public SupportSQLiteDatabase getWritableDatabase() {
// try {
// return (SupportSQLiteDatabase) Origin.call();
// } catch (Throwable e) {
// try {
// FirebaseCrashlyticsProxy.INSTANCE.recordException(e);
// } catch (Throwable ignore) {
// }
// throw e;
// }
// }
@TargetClass("com.facebook.animated.giflite.draw.MovieFrame")
public void renderGifFrame(int w, int h, Bitmap bitmap) {
try {
Origin.callVoid();
} catch (Throwable e) {
e.printStackTrace();
}
}
@Insert("setAlarm")
@TargetClass("androidx.work.impl.utils.ForceStopRunnable")
static void setAlarmFix(Context context) {
try {
Origin.callVoid();
} catch (Throwable e) {
e.printStackTrace();
}
}
@Insert("updateScrollEventValues")
@TargetClass("androidx.viewpager2.widget.ScrollEventAdapter")
private void fixUpdateScrollEvent() {
try {
Origin.callVoid();
} catch (Throwable e) {
e.printStackTrace();
}
}
@Insert("closeQuietly")
@TargetClass("okhttp3.internal.Util")
public static void fixCloseQuietly(Socket socket) {
try {
Origin.callVoid();
} catch (Throwable e) {
e.printStackTrace();
}
}
/* @Insert("onActivityCreated")
@TargetClass("com.google.firebase.messaging.FcmLifecycleCallbacks")
public void fixFcmOnCreate(Activity var1, Bundle var2) {
try {
Origin.callVoid();
} catch (Exception e) {
try {
FirebaseCrashlyticsProxy.INSTANCE.recordException(e);
} catch (Exception ignore) {
}
e.printStackTrace();
}
}*/
//Fatal Exception: java.util.NoSuchElementException
// @TargetClass("okhttp3.internal.connection.ConnectInterceptor")
// public okhttp3.Response fixFindConnection(Interceptor.Chain chain) throws IOException {
// try {
// return (okhttp3.Response) Origin.call();
// } catch (Throwable e) {
// try {
// FirebaseCrashlyticsProxy.INSTANCE.recordException(e);
// } catch (Throwable ignore) {
// }
// if (!(e instanceof IOException)) {
// throw new IOException(e);
// } else {
// throw e;
// }
// }
// }
@Insert("tryIntent")
@TargetClass("com.facebook.login.NativeAppLoginMethodHandler")
protected boolean fixTryIntent(Intent intent, int requestCode) {
try {
return (Boolean) Origin.call();
} catch (Throwable e) {
e.printStackTrace();
return false;
}
}
@Insert("prefetchPositionWithDeadline")
@TargetClass("androidx.recyclerview.widget.GapWorker")
private RecyclerView.ViewHolder fixRecyclerViewDeadline(RecyclerView view, int position, long deadlineNs) {
try {
return (RecyclerView.ViewHolder) Origin.call();
} catch (Throwable e) {
e.printStackTrace();
return null; //返回null,reclerView某些情况也会出错崩溃
}
}
//临时修复
@Insert("dispatchStop")
@TargetClass("androidx.fragment.app.FragmentController")
public void fixFragmentDispatchStop() {
try {
Origin.callVoid();
} catch (Exception e) {
e.printStackTrace();
}
}
@Insert("isForceStopped")
@TargetClass("androidx.work.impl.utils.ForceStopRunnable")
public boolean fixForceStopped() {
try {
return (Boolean) Origin.call();
} catch (Throwable e) {
return false;
}
}
@TargetClass(value = "com.facebook.login.LoginFragment")
@Insert(value = "onActivityResult")
public void fixFacebookLoginResult(int requestCode, int resultCode, Intent data) {
try {
Origin.callVoid();
} catch (Throwable e) {
e.printStackTrace();
}
}
@TargetClass(value = "com.google.android.material.resources.TypefaceUtils")
@Insert(value = "maybeCopyWithFontWeightAdjustment")
public static Typeface fixTypefaceCrash(android.content.res.Configuration configuration, Typeface typeface) {
try {
return (Typeface) Origin.call();
} catch (Throwable e) {
return null;
}
}
// @TargetClass("com.huawei.hms.push.HmsMessageService")
// @Insert("handleIntentMessage")
// private void dispatchMessageHuaWei(Intent intent) {
// Log.w("aws_push", "aws_push HmsMessageService dispatchMessage1");
// Origin.callVoid();
// NotifyReport.dispatchMessageT(intent);
// Log.w("aws_push", "aws_push HmsMessageService dispatchMessage2");
// }
@TargetClass("com.tencent.mmkv.MMKV")
@Insert("getAll")
public Map<String, ?> catchMMKVGetAll() {
try {
return (Map<String, ?>) Origin.call();
} catch (Throwable e) {
e.printStackTrace();
return new HashMap<>();
}
}
@TargetClass("com.tencent.mmkv.MMKV")
@Insert("registerOnSharedPreferenceChangeListener")
public void catchMMKVRegisterOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener) {
try {
Origin.callVoid();
} catch (Throwable e) {
if (BuildConfig.DEBUG) {
e.printStackTrace();
}
}
}
@TargetClass("com.tencent.mmkv.MMKV")
@Insert("unregisterOnSharedPreferenceChangeListener")
public void catchMMKVUnregisterOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener) {
try {
Origin.callVoid();
} catch (Throwable e) {
e.printStackTrace();
}
}
//启动页res/drawable/abc_vector_test.xml 图片加载失败,导致启动页崩溃
@Insert("getDrawableIfKnown")
@TargetClass("androidx.appcompat.widget.TintTypedArray")
public Drawable fixVectorCrash(int index) {
try {
return (Drawable) Origin.call();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
//商详页大图模块FragmentStateAdapter onStateChanged导致的崩溃
@Insert("consumeRestoredStateForKey")
@TargetClass("androidx.savedstate.SavedStateRegistry")
public Bundle consumeRestoredStateForKey(String key) {
if (BuildConfig.DEBUG) {
return (Bundle) Origin.call();
} else {
try {
return (Bundle) Origin.call();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
//商详/拍照等页面大图片会Crash
@Insert("draw")
@TargetClass("com.facebook.drawee.drawable.ForwardingDrawable")
public void draw(Canvas canvas) {
boolean check = true;
try {
Object o = This.get();
check = ForwardingDrawableCheckUtils.check(o);
} catch (Exception e) {
e.printStackTrace();
}
if (check) {
Origin.callVoid();
}
}
//购物车刷新推荐列表 mChildHelper为空导致firebase少量崩溃
@Insert("removeView")
@TargetClass("androidx.recyclerview.widget.RecyclerView$LayoutManager")
public void removeView(View child) {
try {
Origin.callVoid();
} catch (Exception e) {
e.printStackTrace();
}
}
// 列表滑动回收的时候,抛异常,捕获崩溃
@Insert("recycleViewHolderInternal")
@TargetClass("androidx.recyclerview.widget.RecyclerView$Recycler")
public void recycleViewHolderInternal(RecyclerView.ViewHolder holder) {
try {
Origin.callVoid();
} catch (Exception e) {
e.printStackTrace();
}
}
/*
*
* fix appsflyer sdk 6.10.3
*/
@TargetClass(value = "com.appsflyer.internal.AFd1pSDK$4", scope = Scope.SELF)
@Insert(value = "run")
public void run() {
try {
Origin.callVoid();
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意坑
Module requires ASM6
版本太旧
官方的版本太旧没维护了,不支持 7.x+ 高版本的;AGP8.0 不支持
需要自己 fork 维护,特别是 AGP8.0 后,Transformer API 移除了
不支持类的构造方法 hook
@Proxy("<init>")
@TargetClass("com.example.lancetdemos.MyOkhttpClient")
void OkHttpClient(MyOkhttpClient.Builder builder) {
builder.i = 10024;
Origin.callVoid();
}